package no.netlight.gotogate;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.PropertyContainer;

public class Route {
	private List<PropertyContainer> routeElements;
	private ArrayList<Integer> indices;
	private ArrayList<HashMap<String, String>> segments;
	private int transfers;
	private int totalTime;

	public Route() {
		routeElements = new ArrayList<PropertyContainer>();
		indices = new ArrayList<Integer>();
		segments = new ArrayList<HashMap<String, String>>();
		transfers = 0;
		totalTime = 0;
	}

	public Route(List<PropertyContainer> routeElements,
			ArrayList<Integer> indices, int totalTime, int transfers) {
		this.routeElements = routeElements;
		this.indices = indices;
		this.transfers = transfers;
		this.totalTime = totalTime;
		segments = new ArrayList<HashMap<String, String>>();
		findSegments();
	}

	/**
	 * get the total time for this route
	 * 
	 * @return int total time of this route
	 */
	public int getTotalTime() {
		return totalTime;
	}
	
	public int getTransfers() {
		return transfers;
	}

	public ArrayList<HashMap<String, String>> getSegments() {
		return segments;
	}
	
	/**
	 * Method for manually building a route. if you did not use the empty constructor, be careful with this method. 
	 * 
	 * @param segment HashMap<String, String> containing the properties "from", "to", "transporttype", "dep", "arr" for each segment
	 */
	public void addSegment(HashMap<String, String> segment){
		if(validSegment(segment)){
			segments.add(segment);
			int depTimeH = Integer.parseInt(segment.get("dep").substring(0, 2));
			int depTimeS = Integer.parseInt(segment.get("dep").substring(2, 4));
			int arrTimeH = Integer.parseInt(segment.get("arr").substring(0, 2));
			int arrTimeS = Integer.parseInt(segment.get("arr").substring(2, 4));
			if(segments.size() > 1){
				depTimeH = Integer.parseInt(segments.get(0).get("dep").substring(0, 2));
				depTimeS = Integer.parseInt(segments.get(0).get("dep").substring(2, 4));
			}
			totalTime = (arrTimeH - depTimeH)*60 + arrTimeS - depTimeS; 
			if(segments.size() > 1){
				if(!segment.get("type").equals(segments.get(segments.size()-2).get("type"))){
					transfers++;
				}
			}
		}
	}
	/**
	 * Adds a segment at position index of the route, segment will not be counted in 
	 * total time or transfers. Used for at-the-airport information.
	 */
	public void addSegment(HashMap<String, String> segment, int index){
		segments.add(index,segment);
	}
	/**
	 * Adds a segment to the beginning of the route, will not be counted in 
	 * total time or transfers. Used for at-the-airport information.
	 * @param from
	 * @param to
	 * @param type
	 * @param startTime
	 * @param endTime
	 */
	public void addSegment(String from, String to, String type, String startTime, String endTime){
		HashMap<String,String> segment = new HashMap<String, String>();
    	segment.put("from", from);
    	segment.put("to", to); 
    	segment.put("type", type);
    	segment.put("dep", startTime);
    	segment.put("arr", endTime);
    	segments.add(0,segment);
	}

	private boolean validSegment(HashMap<String, String> segment){
		try{
			int arr = Integer.parseInt(segment.get("arr"));
			int dep = Integer.parseInt(segment.get("dep"));
			
			boolean stopExists = false;
			for (HashMap<String, String> seg : segments) {
				stopExists = seg.get("from").equals(segment.get("from"));
				if(stopExists)
					throw new Exception();
			
				stopExists = seg.get("to").equals(segment.get("to"));
				if(stopExists)
					throw new Exception();
			}
			
			if(segments.contains(segment) || dep > arr){
				throw new Exception();
			}
		}catch (Exception e) {
			return false;
		}
		return true;
	}
	
	public void removeSegment(int index) {
		segments.remove(index);
	}
	
	public void removeSegment(HashMap<String, String> segment) {
		if(segments.contains(segment)){
			segments.remove(segment);
		}
	}

	private void findSegments() {
		int counter = indices.size() - 1;
		Node currentStartNode = null;
		String currentStartTime = null;
		Node currentEndNode = null;
		String currentEndTime = null;
		String previousType = null;
		String res = "";
		for (int i = 1; i < routeElements.size(); i += 2) {
			if (previousType == null) {
				// init segment
				currentStartNode = (Node) routeElements.get(i - 1);
				currentStartTime = ((String[]) routeElements.get(i)
						.getProperty("departuretimes"))[indices.get(counter)];
				previousType = (String) routeElements.get(i)
						.getProperty("name");
				currentEndNode = (Node) routeElements.get(i + 1);
				currentEndTime = ((String[]) routeElements.get(i).getProperty(
						"arivaltimes"))[indices.get(counter)];
			} else if (!previousType.equals((String) routeElements.get(i)
					.getProperty("name"))) {
				// print finished segment
				/*
				 * res += "Take " + previousType + " which leaves " +
				 * currentStartNode.getProperty("name") + " at " +
				 * currentStartTime + " and arrives at " +
				 * currentEndNode.getProperty("name") + " at " + currentEndTime;
				 */
				//Add finished segment
				HashMap<String, String> segment = new HashMap<String, String>();
				segment.put("from", (String) currentStartNode
						.getProperty("name"));
				segment.put("to", (String) currentEndNode.getProperty("name"));
				segment.put("type", previousType);
				segment.put("dep", currentStartTime);
				segment.put("arr", currentEndTime);
				segments.add(segment);
				// begin new segment
				currentStartNode = (Node) routeElements.get(i - 1);
				currentStartTime = ((String[]) routeElements.get(i)
						.getProperty("departuretimes"))[indices.get(counter)];
				previousType = (String) routeElements.get(i)
						.getProperty("name");
				currentEndNode = (Node) routeElements.get(i + 1);
				currentEndTime = ((String[]) routeElements.get(i).getProperty(
						"arivaltimes"))[indices.get(counter)];

			} else {
				// extend current segment
				currentEndNode = (Node) routeElements.get(i + 1);
				currentEndTime = ((String[]) routeElements.get(i).getProperty(
						"arivaltimes"))[indices.get(counter)];
			}
			counter--;
		}
		// print out the very last segment
		/*
		 * res += "Take " + previousType + " which leaves " +
		 * currentStartNode.getProperty("name") + " at " + currentStartTime +
		 * " and arrives at " + currentEndNode.getProperty("name") + " at " +
		 * currentEndTime;
		 */
		HashMap<String, String> segment = new HashMap<String, String>();
		segment.put("from", (String) currentStartNode.getProperty("name"));
		segment.put("to", (String) currentEndNode.getProperty("name"));
		segment.put("type", previousType);
		segment.put("dep", currentStartTime);
		segment.put("arr", currentEndTime);
		segments.add(segment);

	}

	public String returnRouteInfo() {
		String res = "";
		for (HashMap<String, String> seg : segments) {
			res += "Take " + seg.get("type") + " which leaves "
					+ seg.get("from") + " at " + seg.get("dep")
					+ " and arrives at " + seg.get("to") + " at "
					+ seg.get("arr") + "\n";
		}
		res += "Total time: " + this.totalTime + " and number of transfers: " + this.transfers + "\n";
		return res;
	}

	/**
	 * Method only used for testing
	 * @return
	 */
	protected List<PropertyContainer> getRouteElements(){
		return routeElements;
	}
}
